#include "PlayerControllerComponent.h"
#include "GameObject.h"
#include "VisualComponent.h"
#include "PhysicsComponent.h"
#include "AnimationComponent.h"
#include "SensorComponent.h"
#include "TempVisualComponent.h"
#include "..\Factory\PlayerProjectileFactory.h"
#include "..\Generic\IntMessage.h"
#include "..\Generic\MessageSystem.h"
#include "..\Components\FlashComponent.h"
#include "..\Managers\GameObjectManager.h"
#include "..\Managers\ResourceManager.h"
#include "..\Managers\EventManager.h"
#include "..\Generic\Event.h"
#include "..\Managers\StateManager.h"
#include "..\States\MenuState.h"

PlayerControllerComponent::PlayerControllerComponent() : m_jumpStrength(45.0f), m_maxSpeed(1.0f), m_desiredXVel(0.0f), m_acceleration(0.1f),
	m_deacceleration(0.8f), m_timer(0.0f), m_isJumping(false), m_isShooting(false),  m_isClimbing(false), m_isSliding(false), m_animationState(ANIM_IDLE), m_maxBullets(3), m_currentBulletCount(0),
	m_jumpTimer(250), m_elapsedJumpTime(0), m_canJump(false), m_bulletVelocity(4.0f), m_receivedDamage(false), m_currentState(PLAYER_STATE_NORMAL), m_eventManager(nullptr)
{
	m_funcStatePtr[PLAYER_STATE_NORMAL] = &PlayerControllerComponent::normal;
	m_funcStatePtr[PLAYER_STATE_DEAD] = &PlayerControllerComponent::dead;
}

PlayerControllerComponent::~PlayerControllerComponent() 
{ 
	ResourceManager::getSingleton().getAudioEngine()->destroySoundBuffer("jump");
	ResourceManager::getSingleton().getAudioEngine()->destroySoundBuffer("land");
	ResourceManager::getSingleton().getAudioEngine()->destroySoundBuffer("damage_rcv");
	ResourceManager::getSingleton().getAudioEngine()->destroySoundBuffer("shoot");
	delete m_land;
	delete m_shoot;
	delete m_damage;
	delete m_jump;
	delete m_projectileFactory;
	MessageSystem::getSingleton().unregister("bullet", this);
	if (m_eventManager != nullptr)
	{
		delete m_eventManager;
		m_eventManager = nullptr;
	}
}

void PlayerControllerComponent::update(const GameTime& p_gameTime)
{
	(this->*m_funcStatePtr[m_currentState])(p_gameTime);
}

inline void PlayerControllerComponent::move(b2Vec2& velocity, PhysicsComponent* physicsComponent)
{
	float velXChange = m_desiredXVel - velocity.x;
	float impulseX = physicsComponent->getBody()->GetMass() * velXChange;
	physicsComponent->getBody()->ApplyLinearImpulse(b2Vec2(impulseX, 0.0f), physicsComponent->getBody()->GetWorldCenter());
}

void PlayerControllerComponent::init()
{
	m_shoot = ResourceManager::getSingleton().getAudioEngine()->loadSound("shoot");
	m_jump = ResourceManager::getSingleton().getAudioEngine()->loadSound("jump");
	m_damage = ResourceManager::getSingleton().getAudioEngine()->loadSound("damage_rcv");
	m_land = ResourceManager::getSingleton().getAudioEngine()->loadSound("land");

	IComponent* sensorComponent = getOwner()->getComponent("SensorComponent");
	SensorComponent* sensor = static_cast<SensorComponent*>(sensorComponent);

	std::function<void()> func = [this]{m_isJumping = false; m_canJump = true; m_elapsedJumpTime = 0; m_receivedDamage = false; m_land->play(); };
	sensor->registerBeginContactCallback(func);
	/*func = [this]
	{ 
		if (!m_isJumping)
		{
			m_isJumping = true;
			m_canJump = false;
		}
	};*/
	sensor->registerEndContactCallback(func);

	m_projectileFactory = new PlayerProjectileFactory;

	MessageSystem::getSingleton().registerSubject<PlayerControllerComponent>("bullet", this, &PlayerControllerComponent::bulletCallBack);

	IComponent* ci = getOwner()->getComponent("VisualComponent");
	m_visual = static_cast<VisualComponent*>(ci);
	ci = getOwner()->getComponent("PhysicsComponent");
	m_physics = static_cast<PhysicsComponent*>(ci);
	ci = getOwner()->getComponent("AnimationComponent");
	m_animations = static_cast<AnimationComponent*>(ci);
	m_animations->getAnimationManager()->setNextAnimation("idle", "blink");
	m_animations->getAnimationManager()->setNextAnimation("blink", "idle2");
	m_animations->getAnimationManager()->setNextAnimation("idle2", "blink2");
	m_animations->getAnimationManager()->setNextAnimation("blink2", "idle");
	m_flash = static_cast<FlashComponent*>(getOwner()->getComponent("FlashComponent"));


}

void PlayerControllerComponent::bulletCallBack(const std::string& p_id, Message* p_message)
{
	IntMessage* msg = static_cast<IntMessage*>(p_message);
	m_currentBulletCount += msg->getValue();
}

void PlayerControllerComponent::hit()
{
	if (m_currentState == PLAYER_STATE_NORMAL)
	{
		m_damage->play();

		std::function<void()> func = [this] 
		{
			float direction = m_visual->getSprite()->getScale().x * -1.0f;
			float impulse = m_physics->getBody()->GetMass() * 1.1f;
			m_physics->getBody()->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
			m_physics->getBody()->ApplyLinearImpulse(b2Vec2(impulse * direction, -impulse), m_physics->getBody()->GetWorldCenter());
		};
		PhysicsManager::getSingleton().addEvent(func);
		m_receivedDamage = true;
		m_moveState = MS_NONE;
		m_animationState = ANIM_DAMAGE;
		TempVisualComponent* tvc = new TempVisualComponent(1, "dmg", "animations\\hit.xml", 1.9f, true);
		getOwner()->addComponent(tvc);
		tvc->updatePosition();
		tvc = new TempVisualComponent(1, "dmg_shock", "animations\\hit_shock.xml", 1.9f, false);
		tvc->setPosition(getOwner()->getPosition().x, getOwner()->getPosition().y - 20.0f);
		getOwner()->addComponent(tvc);
		m_flash->setActive(true);
	}
}

void PlayerControllerComponent::normal(const GameTime& p_gameTime)
{
		b2Vec2 vel = m_physics->getBody()->GetLinearVelocity();
		
		if (!m_receivedDamage)
		{
			if(InputManager::getSingleton().isKeyDown("left"))
			{
				m_moveState = MS_LEFT;
			}
			else if(InputManager::getSingleton().isKeyDown("right"))
			{
				m_moveState = MS_RIGHT;
			}
			else
			{
				m_moveState = MS_STOP;
				m_animationState = ANIM_IDLE;
			}

			if(InputManager::getSingleton().isKeyDown("jump") && m_canJump && !m_isJumping)
			{
				m_isJumping = true;
				m_jump->play();
			}

			/*if (InputManager::getSingleton().isKeyReleased("jump") && m_isJumping)
			{
				m_isJumping = false;
				m_elapsedJumpTime = 0;
			}*/

			if(InputManager::getSingleton().isKeyPressed("shoot") && m_currentBulletCount < m_maxBullets)
			{
				m_shoot->play();
				m_timer = 0.0f;
				m_isShooting = true;
				GameObject* go = m_projectileFactory->createProjectile(vn::Vector2(m_visual->getSprite()->getPosition()), vn::Vector2(m_bulletVelocity * m_visual->getSprite()->getScale().x, 0.0f));
				getOwner()->getGameObjectManager()->addGameObject(go);
				go->setGameObjectManager(getOwner()->getGameObjectManager());
			}
		}

		switch(m_moveState)
		{
		case MS_LEFT:
			{
				m_animationState = ANIM_WALKING;
				m_desiredXVel = b2Max(vel.x - m_acceleration, -m_maxSpeed);
				move(vel, m_physics);
				m_visual->getSprite()->setScale(-1.0f, 1.0f);
				break;
			}
		case MS_RIGHT:
			{
				m_animationState = ANIM_WALKING;
				m_desiredXVel = b2Min(vel.x + m_acceleration, m_maxSpeed);
				move(vel, m_physics);
				m_visual->getSprite()->setScale(1.0f, 1.0f);
				break;
			}	
		case MS_STOP:
			{
				m_desiredXVel = vel.x * m_deacceleration;
				move(vel, m_physics);
				break;
			}
		}

		if(m_isJumping)
		{
			m_animationState = ANIM_JUMP;
			if(InputManager::getSingleton().isKeyDown("jump") && m_canJump)
			{
				float impulse = m_physics->getBody()->GetMass() * m_jumpStrength;
				m_physics->getBody()->SetLinearVelocity(b2Vec2(m_physics->getBody()->GetLinearVelocity().x, -impulse));
				m_elapsedJumpTime = std::min(m_elapsedJumpTime + p_gameTime.getElapsedTimeAsMilliseconds(), m_jumpTimer);
				if (m_elapsedJumpTime >= m_jumpTimer)
				{
					m_canJump = false;
					m_elapsedJumpTime = 0;
				}
			}
			else if(InputManager::getSingleton().isKeyReleased("jump"))
			{
				m_canJump = false;
			}
		}

		if (m_receivedDamage)	// ugh, fix this
			m_animationState = ANIM_DAMAGE;

#pragma region animationStates
			switch(m_animationState)
			{
			case ANIM_IDLE:
				{
					if(m_isShooting)
					{
						m_animations->setAnimation("shoot_stand");
					}
					else
						m_animations->setAnimation("idle");

					break;
				}
			case ANIM_WALKING:
				{
					if(m_isShooting)
					{
						m_animations->setAnimation("shoot_walk");
					}
					else
						m_animations->setAnimation("walk");

					break;
				}
			case ANIM_SLIDING:
				{
					m_animations->setAnimation("slide");
					break;
				}
			case ANIM_JUMP:
				{
					if(m_isShooting)
					{
						m_animations->setAnimation("shoot_jump");
					}
					else
						m_animations->setAnimation("jump");
					break;
				}
			case ANIM_CLIMB:
				{
					if(m_isShooting)
					{
						m_animations->setAnimation("shoot_climb");
					}
					else
						m_animations->setAnimation("climb");

					break;
				}
			case ANIM_DAMAGE:
				{
					m_animations->setAnimation("damage");
				}
			default:
				break;
			
			}
#pragma endregion

		if(m_isShooting)
		{
			m_timer = std::min(m_timer + p_gameTime.deltaTime(), 1.0f);
			if(m_timer >= 0.5f)
			{
				m_timer = 0.0f;
				m_isShooting = false;
			}
		}
}

void PlayerControllerComponent::dead(const GameTime& p_gameTime)
{
	m_eventManager->update(p_gameTime);
}

void PlayerControllerComponent::die()
{
	m_currentState = PLAYER_STATE_DEAD;
	m_animations->getAnimationManager()->setAnimation("damage");
	m_eventManager = new EventManager;
	std::function<void()> func = [this] {
		explode();
		m_visual->getSprite()->setDoRender(false);
	};
	m_eventManager->addEvent(new vn::Event(func, 0.5f));
	func = [this] { quitGame(); };
	m_eventManager->addEvent(new vn::Event(func, 3.0f));
	m_physics->getBody()->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
	PhysicsManager::getSingleton().clearEvents();
}

void PlayerControllerComponent::explode()
{
	TempVisualComponent* tvc = new TempVisualComponent(1, "explo_dead", "animations/explosion_dead.xml", 1.9f, true);
	getOwner()->addComponent(tvc);
	tvc->updatePosition();
}

void PlayerControllerComponent::quitGame()
{
	StateManager::getSingleton().setState(new MenuState);
}